package com.util;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static java.util.Arrays.asList;

/**
 * 用来生成封装类
 * <p>
 * 还未测试是否支持多态，需要了解method.equals方法
 * 暂时没有添加异常，所生成的类没有抛出异常
 * Created by yangliwei on 2017/7/24.
 */
public class WrapperUtil {


    /**
     * 通过当前类和父接口实现基于当前类和父接口的封装类
     *
     * @param clazz
     * @param superInterface
     */
    public static void createWrapper(Class clazz, Class superInterface) throws IOException {
        File file = createWrapperClassFile(clazz);
        FileOutputStream out = null;
        PrintWriter writer = null;
//        这里直接调用接口的方法，
//        List<Method> methods = getMethods(clazz, superInterface);
        List<Method> methods = Arrays.asList(superInterface.getMethods());
        File oldfile = openOldFile(clazz);
        FileInputStream in = null;
        Reader reader = null;
        StringBuilder sb = null;
        String filed = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
        try {
            out = new FileOutputStream(file);
            writer = new PrintWriter(out);
            writer.println(clazz.getPackage() + ";");
            in = new FileInputStream(oldfile);
            reader = new BufferedReader(new InputStreamReader(in));
            copyImport((BufferedReader) reader, writer);
            writer.println("public class " + clazz.getSimpleName() + "Wrapper implements "+superInterface.getSimpleName()+"{");
            writer.println("private " + clazz.getSimpleName() + " " + filed + ";");
            for (Method method : methods) {
                //因为这里使用的是通过接口获取的方法，所以需要去掉abstract权限
                writer.print(Modifier.toString(method.getModifiers()).replace("abstract","") + " " + method.getReturnType().getSimpleName() + " " + method.getName());
                sb = getParametersAndTypeString(method);
                writer.print(sb.toString());
                writer.println("{");
                if (!method.getReturnType().equals(void.class)) {
                    writer.print("return ");
                }
                writer.println("this." + filed + "." + method.getName()  + getParametersString(method) + ";");
                writer.println("}");
            }
            writer.println("}");


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.close();
            }
            if (out != null) {
                out.close();
            }
        }


    }


    /**
     * 获取完整参数列表
     * 形式 (ParameterType parameterName,ParameterType parameter2......)
     *
     * @param method
     * @return
     */
    public static StringBuilder getParametersAndTypeString(Method method) {
        StringBuilder sb = new StringBuilder("(");
        int temp = 0;
        for (Parameter parameter : method.getParameters()) {
            sb.append(parameter.getType().getSimpleName() + " " + parameter.getName());
            temp++;
            if (temp != method.getParameters().length) {
                sb.append(",");
            }
        }
        sb.append(")");
        return sb;
    }

//    没有数据类型的参数列表
    public static StringBuilder getParametersString(Method method) {
        StringBuilder sb = new StringBuilder("(");
        int temp = 0;
        for (Parameter parameter : method.getParameters()) {
            sb.append(parameter.getName());
            temp++;
            if (temp != method.getParameters().length) {
                sb.append(",");
            }
        }
        sb.append(")");
        return sb;
    }


    /**
     * 复制源文件里面导入的包*
     *
     * @param reader
     * @param writer
     */
    private static void copyImport(BufferedReader reader, PrintWriter writer) {
        try {
            String line = null;
            while ((line = reader.readLine()) != null) {
//                readLine方法后面自动去掉了换行符
//            if (line.startsWith("import") && line.endsWith(System.getProperty("line.separator")))
                if (line.startsWith("import"))
                    writer.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //打开当前类的文件
    private static File openOldFile(Class clazz) {
        String filePath = getOldFilePath(clazz);
        File file = new File(filePath + ".java");
        return file;
    }

    //获取当前文件路径
    //没有.java后缀在打开文件是的时候需要手动添加
    public static String getOldFilePath(Class clazz) {
        String rootPath = System.getProperty("user.dir");
        String pacakgeName = clazz.getCanonicalName();
        String filePath = rootPath + File.separator + "src" + File.separator + pacakgeName.replace(".", File.separator);
        return filePath;
    }

    /**
     * 通过当前类创建一个封装类文件，与当前类在同一个包里
     *
     * @param clazz
     */
    private static File createWrapperClassFile(Class clazz) {
        String filePath = getOldFilePath(clazz) + "Wrapper.java";
        File file = new File(filePath);
        try {
            file.createNewFile();
        } catch (IOException e) {
            System.out.println(filePath + "file create failed");
        }
        return file;
    }


    /**
     * 通过当前类与父类接口的的所有匹配方法方法
     * 这个方法错了，因为两个Method对象比较的时候，必须同一个类里面的一个方法才可相同
     *
     * @param clazz
     * @return
     */
    private static List<Method> getMethods(Class clazz, Class superInterface) {
        Method[] classMethods = clazz.getMethods();
        Method[] superInterfaceMethods = superInterface.getMethods();
        List<Method> classList = asList(classMethods);
        List<Method> superInterfaceList = asList(superInterfaceMethods);
        List<Method> result = new ArrayList<>();
        for (int i = 0; i < classList.size(); i++) {
            for (int j = 0; j < superInterfaceList.size(); j++) {
                if (classList.get(i).equals(superInterfaceList.get(j))) {
                    result.add(classList.get(i));
                }
            }
        }

        return result;
    }


}
